package at.donalfonso.fahrer;

import java.util.ArrayList;
import java.util.Random;

import org.mozartspaces.capi3.CoordinationData;
import org.mozartspaces.capi3.KeyCoordinator;
import org.mozartspaces.capi3.LabelCoordinator;
import org.mozartspaces.capi3.LindaCoordinator;
import org.mozartspaces.capi3.Selector;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.CapiUtil;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsConstants.RequestTimeout;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.MzsTimeoutException;
import org.mozartspaces.core.TransactionReference;

import at.donalfonso.model.LieferBestellung;
import at.donalfonso.model.LieferBestellungStatus;
import at.donalfonso.model.LieferKunde;
import at.donalfonso.model.LieferKundeStatus;
import at.donalfonso.utility.SpaceUtility;

public class Fahrer {


	private SpaceUtility space;
	private Capi capi;

	private String name;
	
	private int waitTime = 3000;
	
	/**
	 * Ein Fahrer wartet auf fertige Lieferbestellungen und liefert diese
	 * an die angegeben Adresse. 
	 * Der Status (erfolgreich/gescheitet) wird an das Pizzagui gemeldet.
	 */
	public static void main(String[] args) {
		Fahrer fahrer;
		if (args.length < 3) {
			System.out.println("Usage: java Fahrer <ID> <FILIAL_ID> <Benchmark=1>");
			fahrer = new Fahrer("F" + new Random().nextInt(100), 1, 0);	
		} 
		else {
			fahrer = new Fahrer(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]));
		}

		fahrer.run();
	}

	public Fahrer(String name, int filiale, int benchmark) {
		space = new SpaceUtility(filiale);
		capi = space.getCapi();
		this.name = name;

		if (benchmark > 0) {
			waitTime = 0;
		}
		
		System.out.println("Fahrer: " + name + " (Filiale " + filiale + ")");
	}

	public void run() {
		
		while (true) {
			
			try {
//				System.out.println(name + " alive");
				Selector sel = LabelCoordinator.newSelector(LieferBestellung.class.getName());
				
				TransactionReference tx = capi.createTransaction(10000, space.getUri());
				ArrayList<LieferBestellung> lb = null;
				try {
					lb = capi.take(space.getBestellungFertigContainer(), sel, 2000, tx);
				} catch (MzsTimeoutException e) {
					// okay
					capi.rollbackTransaction(tx);
					continue;
				}
				LieferBestellung bestellung = CapiUtil.getSingleEntry(lb);
				
				System.out.println("Lieferbestellung zum Ausliefern: " + bestellung);
				
				bestellung.setFahrer(name);
				bestellung.setLieferstatus(LieferBestellungStatus.WIRD_GELIEFERT);
				CoordinationData coord = KeyCoordinator.newCoordinationData(bestellung.getBestellungId());
				// ohne Transaction für sofortige Sichtbarkeit des Zwischenstatus
				capi.write(new Entry(bestellung, coord), space.getZustellungContainer());
				
				Thread.sleep(waitTime);
				
				if (bestellung.adresseExistiert()) {
					String adresse = bestellung.getAdresse();
					ContainerReference kundenCont = space.getLieferCapi().lookupContainer("kunde." + adresse, space.getLieferUri(), RequestTimeout.TRY_ONCE, null);
					
					bestellung.setLieferstatus(LieferBestellungStatus.ERFOLGREICH);
					capi.write(new Entry(bestellung), kundenCont);
					System.out.println("Bestellung zugestellt.");
				}
				else {
					// Fehler melden.
					bestellung.setLieferstatus(LieferBestellungStatus.GESCHEITERT);
					System.out.println("Zustellung gescheitert.");
				}
				// Bestellung am PizzeriaController updaten
				capi.delete(space.getZustellungContainer(), KeyCoordinator.newSelector(bestellung.getBestellungId()), RequestTimeout.TRY_ONCE, tx);
				capi.write(new Entry(bestellung, LindaCoordinator.newCoordinationData()), space.getSaalContainer(), RequestTimeout.TRY_ONCE, tx);
				capi.commitTransaction(tx);

				// LieferKunde am GroupController updaten, geht nur bei Bestellungen aus eigener
				// Filiale
				if (bestellung.getLoadBalancer().isEmpty()) {
					TransactionReference tx1 = capi.createTransaction(100, space.getUri());
					ArrayList<LieferKunde> llk = capi.take(space.getSaalContainer(), KeyCoordinator.newSelector(bestellung.getGruppenId()), RequestTimeout.TRY_ONCE, tx1);
					if (llk == null) {
						capi.rollbackTransaction(tx1);
						return;
					}
					LieferKunde lk = CapiUtil.getSingleEntry(llk);
					if (bestellung.adresseExistiert())
						lk.setStatus(LieferKundeStatus.BEZAHLT);
					capi.write(new Entry(lk), space.getSaalContainer(), RequestTimeout.TRY_ONCE, tx1);
					capi.commitTransaction(tx1);
				}
				else {
					// TODO: LieferKunde aus anderer Filiale
				}
			
			} catch (MzsTimeoutException e) {
			
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (MzsCoreException | NullPointerException e) {
				e.printStackTrace();
			}
		}
	}
}
